单例模式 - waterystone - 博客园

创建时间:2018/7/27 15:19
来源:https://www.cnblogs.com/waterystone/p/5546312.html

waterystone

you can NEVER be too careful~

随笔 - 67  文章 - 0  评论 - 36

单例模式

一、懒汉式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class Singleton {
    private volatile static Singleton instance; //声明成 volatile
    private Singleton (){}
 
    public static Singleton getSingleton() {
        if (instance == null) {                        
            synchronized (Singleton.class) {
                if (instance == null) {      
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
 
}

  使用DCL进行锁的双重检查。这里的volatile是亮点:设置内存屏障,防止instance未初始化完就被泄漏出来。(volatile语义不清楚的可能参考:Java并发编程:volatile关键字解析)

 

二、饿汉式

1
2
3
4
5
6
7
8
9
10
public class Singleton{
    //类加载时就初始化
    private static final Singleton instance = new Singleton();
 
    private Singleton(){}
 
    public static Singleton getInstance(){
        return instance;
    }
}

  这种写法简单明了,也线程安全。缺点是它不是一种懒加载模式(lazy initialization),单例会在加载类后一开始就被初始化,即使客户端没有调用 getInstance()方法。

 

三、静态内部类

1
2
3
4
5
6
7
8
9
public class Singleton { 
    private static class SingletonHolder { 
        private static final Singleton INSTANCE = new Singleton(); 
    
    private Singleton (){} 
    public static final Singleton getInstance() { 
        return SingletonHolder.INSTANCE;
    
}

  这种写法仍然使用JVM本身机制保证了线程安全问题;由于 SingletonHolder 是私有的,除了 getInstance() 之外没有办法访问它,因此它是懒汉式的;同时读取实例的时候不会进行同步,没有性能缺陷;也不依赖 JDK 版本。

 

四、总结

  一般情况下直接使用饿汉式就好了,如果明确要求要懒加载(lazy initialization)会倾向于使用静态内部类。

  但以上几种方式都无法保证反射出来还是单例。可以通过如下代码验证:

1
2
3
4
5
6
7
8
9
//获得构造器
Constructor con = Singleton.class.getDeclaredConstructor();
//设置为可访问
con.setAccessible(true);
//构造两个不同的对象
Singleton singleton1 = (Singleton)con.newInstance();
Singleton singleton2 = (Singleton)con.newInstance();
//验证是否是不同对象
System.out.println(singleton1.equals(singleton2));

 

  网上流传一种方法,可以利用enum!有了enum语法糖,JVM会阻止反射获取枚举类的私有构造方法。

1
2
3
public enum SingletonEnum {
    INSTANCE;
}

  

 

 

 

参考:

作者:水岩
         
本博客中未标明转载的文章归作者水岩和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
posted @ 2016-05-31 16:26 waterystone 阅读(66) 评论(0) 编辑 收藏
注册用户登录后才能发表评论,请 登录注册访问网站首页。
昵称:waterystone
园龄:3年5个月
粉丝:46
关注:1
<2018年7月>
24252627282930
1234567
891011121314
15161718192021
22232425262728
2930311234

搜索

 
 

最新评论

  • 1. Re:Java并发之AQS详解
  • 你好,请教一个问题,在ThreaLocal的代码中,在getEntryAfterMiss方法中有一段代码: if(k == null) expungeStaleEntry(i)。请问什么时候会出现e ......
  • --源码搬运工
  • 2. Re:Java并发之AQS详解
  • else if (ws == 0 && !compareAndSetWaitStatus(h, 0, Node.PROPAGATE))这句话的作用到底是什么呢 ws==0 是默认状态 还是upark.......
  • --生命潦草
  • 3. Re:Java并发之AQS详解
  • @我本是道源码才是王道,当时看AQS费了不少心血。。。...
  • --waterystone
  • 4. Re:Java并发之AQS详解
  • @$爱上你的唇$在非公平锁时,线程A去acquireQueued时,这时可能有新的线程B没排队,直接把资源抢跑了,这时A还是获取失败。。。...
  • --waterystone
  • 5. Re:Java并发之AQS详解
  • unparkSuccessor那里的标注是不是有争议呢,如果next不为空,则释放next,如果next为空,则从队尾向前查找一个待释放节点释放
  • --南飞的驴
Copyright ©2018 waterystone